//
//  WYVideoCrypt.m
//  VedioDESEncrypt
//
//  Created by Creative on 15/12/9.
//  Copyright © 2015年 Creative. All rights reserved.
//
//  加密方法：将视频文件使用DES方式直接加密。每间隔512K加密512字节。
//  注意：加密文件与解密文件不能是同一个文件。
//

#import "WYCryptDES.h"
#import <CommonCrypto/CommonCryptor.h>

//每间隔500K，进行512字节的加密
#define WY_CRYPT_ENCODE_LENGTH      512
#define WY_CRYPT_BUFFER_LENGTH      1024
#define WY_CRYPT_SKIP_LENGTH        (512 * 1024)
#define WY_CRYPT_SKIP_COUNT         1024

//初始向量
const static Byte wy_des_iv[] = {55,99,12,9,62,34,118,98};

@implementation WYCryptDES

/*Data加密
 *参数
 *data       : 加密明文
 *key        : 密钥，密钥必须等于8位；如超过8位，后面的截断，如不足8位则密码补‘0’
 */
+ (NSData *) encryptData:(NSData *)data key:(NSString *)key
{
    NSData *cipher = nil;
    const char * dataBytes = [data bytes];
    size_t dataLength = [data length];
    size_t bufLength = MAX(dataLength * 2, WY_CRYPT_BUFFER_LENGTH);
    unsigned char * buffer = malloc(bufLength);
    memset(buffer, 0, sizeof(char) * bufLength);
    size_t numBytesEncrypted = 0;
    if ([key length] > kCCKeySizeDES) {
        key = [key substringToIndex:kCCKeySizeDES];
    }
    if ([key length] < kCCKeySizeDES) {
        for (int i = [key length]; i < kCCKeySizeDES; i++) {
            key = [key stringByAppendingString:@"0"];
        }
    }
    CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmDES,
                                          kCCOptionPKCS7Padding,
                                          [key UTF8String], kCCKeySizeDES,
                                          wy_des_iv,
                                          dataBytes, dataLength,
                                          buffer, bufLength,
                                          &numBytesEncrypted);
    if (cryptStatus == kCCSuccess) {
        cipher = [NSData dataWithBytes:buffer length:(NSUInteger)numBytesEncrypted];
    }
    if (buffer) {
        free(buffer); buffer = NULL;
    }
    
    return cipher;
}

/*Data解密
 *参数
 *data       : 加密密文
 *key        : 密钥，密钥必须等于8位；如超过8位，后面的截断，如不足8位则密码补‘0’
 */
+ (NSData *) decryptData:(NSData *)data key:(NSString*)key
{
    NSData* plain = nil;
    const char * dataBytes = [data bytes];
    size_t dataLength = [data length];
    size_t bufLength = MAX(dataLength * 2, WY_CRYPT_BUFFER_LENGTH);
    unsigned char * buffer = malloc(bufLength);
    memset(buffer, 0, sizeof(char) * bufLength);
    size_t numBytesDecrypted = 0;
    if ([key length] > kCCKeySizeDES) {
        key = [key substringToIndex:kCCKeySizeDES];
    }
    if ([key length] < kCCKeySizeDES) {
        for (int i = [key length]; i < kCCKeySizeDES; i++) {
            key = [key stringByAppendingString:@"0"];
        }
    }
    CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt,
                                          kCCAlgorithmDES,
                                          kCCOptionPKCS7Padding,
                                          [key UTF8String],
                                          kCCKeySizeDES,
                                          wy_des_iv,
                                          dataBytes, dataLength,
                                          buffer, bufLength,
                                          &numBytesDecrypted);
    if (cryptStatus == kCCSuccess) {
        plain = [NSData dataWithBytes:buffer length:(NSUInteger)numBytesDecrypted];
    }
    if (buffer) {
        free(buffer); buffer = NULL;
    }
    
    return plain;
}


/*文件加密
 *参数
 *inFile     : 明文文件
 *outFile    : 密文文件
 *key        : 密钥，密钥必须等于8位
 */
+ (BOOL) encryptFile:(NSString *)inFile toFile:(NSString *)outFile withKey:(NSString *)key
{
    BOOL bRet = YES;
    
    NSFileManager * manager = [NSFileManager defaultManager];
    if (![manager fileExistsAtPath:inFile]) {
        return NO;
    }
    
    FILE * in = fopen([inFile cStringUsingEncoding:NSUTF8StringEncoding], "r");
    if (!in) {
        return NO;
    }
    FILE * out = fopen([outFile cStringUsingEncoding:NSUTF8StringEncoding], "w");
    if (!out) {
        fclose(in);
        return NO;
    }
    
    char * buf = malloc(WY_CRYPT_SKIP_LENGTH);
    memset(buf, 0, WY_CRYPT_SKIP_LENGTH);
    
    int length, cryptLength = 0;
    while (YES) {
        length = fread(buf, 1, WY_CRYPT_ENCODE_LENGTH, in);
        NSData * enData = [self encryptData:[NSData dataWithBytes:buf length:length] key:key];
        if (!enData) {
            bRet = NO;
            break;
        }
        cryptLength = [enData length];
        fwrite(&cryptLength, 1, sizeof(int), out);
        fwrite([enData bytes], 1, cryptLength, out);
        if (length < WY_CRYPT_ENCODE_LENGTH) {
            bRet = YES;
            break;
        }

        BOOL finished = NO;
        for (int i = 0; i < WY_CRYPT_SKIP_COUNT; i++) {
            length = fread(buf, 1, WY_CRYPT_SKIP_LENGTH, in);
            fwrite(buf, 1, length, out);
            if (length < WY_CRYPT_SKIP_LENGTH) {
                finished = YES;
                bRet = YES;
                break;
            }
        }
        if (finished)
        {
            break;
        }
    }

    fclose(in); fclose(out);
    free(buf);buf = NULL;
    if (!bRet) {
        if ([manager fileExistsAtPath:outFile]) {
            [manager removeItemAtPath:outFile error:NULL];
        }
    }
    
    return bRet;
}

/*文件解密
 *参数
 *inFile     : 密文文件
 *outFile    : 明文文件
 *key        : 密钥，密钥必须等于8位
 */
+ (BOOL) decryptFile:(NSString *)inFile toFile:(NSString *)outFile withKey:(NSString *)key
{
    BOOL bRet = YES;
    
    NSFileManager * manager = [NSFileManager defaultManager];
    if (![manager fileExistsAtPath:inFile]) {
        return NO;
    }
    
    FILE * in = fopen([inFile cStringUsingEncoding:NSUTF8StringEncoding], "r");
    if (!in) {
        return NO;
    }
    FILE * out = fopen([outFile cStringUsingEncoding:NSUTF8StringEncoding], "w");
    if (!out) {
        fclose(in);
        return NO;
    }
    
    char * buf = malloc(WY_CRYPT_SKIP_LENGTH);
    memset(buf, 0, WY_CRYPT_SKIP_LENGTH);
    
    int length, cryptLength = 0;
    while (YES) {
        length = fread(&cryptLength, 1, sizeof(int), in);
        if (length < sizeof(int)) {
            bRet = YES;
            break;
        }
        if (cryptLength <= 0 || cryptLength > WY_CRYPT_BUFFER_LENGTH) {
            bRet = NO;
            break;
        }
        length = fread(buf, 1, cryptLength, in);
        NSData * enData = [self decryptData:[NSData dataWithBytes:buf length:length] key:key];
        if (!enData) {
            bRet = NO;
            break;
        }
        length = [enData length];
        fwrite([enData bytes], 1, length, out);
        
        BOOL finished = NO;
        for (int i = 0; i < WY_CRYPT_SKIP_COUNT; i++) {
            length = fread(buf, 1, WY_CRYPT_SKIP_LENGTH, in);
            fwrite(buf, 1, length, out);
            if (length < WY_CRYPT_SKIP_LENGTH) {
                finished = YES;
                bRet = YES;
                break;
            }
        }
        if (finished)
        {
            break;
        }
    }
    
    fclose(in); fclose(out);
    free(buf);buf = NULL;
    if (!bRet) {
        if ([manager fileExistsAtPath:outFile]) {
            [manager removeItemAtPath:outFile error:NULL];
        }
    }
    
    return bRet;
}

+ (NSData *) encryptFileData:(NSData *)data key:(NSString *)key {
    return nil;
}
+ (NSData *) decryptFileData:(NSData *)data key:(NSString*)key {
    
    BOOL bRet = YES;

    char * buf = malloc(WY_CRYPT_SKIP_LENGTH);
    memset(buf, 0, WY_CRYPT_SKIP_LENGTH);
    
    NSMutableData * dataOut = [NSMutableData data];
    
    int pos = 0, cryptLength = 0;
    while (YES) {
        @try {
            [data getBytes:&cryptLength range:NSMakeRange(pos, sizeof(int))];
            pos += sizeof(int);
        } @catch (NSException *exception) {
            bRet = YES;
            break;
        } @finally {
        }

        if (cryptLength <= 0 || cryptLength > WY_CRYPT_BUFFER_LENGTH) {
            bRet = NO;
            break;
        }
        
        @try {
            cryptLength = MIN([data length] - pos, cryptLength);
            [data getBytes:buf range:NSMakeRange(pos, cryptLength)];
            pos += cryptLength;
        } @catch (NSException *exception) {
            bRet = NO;
            break;
        } @finally {
        }

        NSData * enData = [self decryptData:[NSData dataWithBytes:buf length:cryptLength] key:key];
        if (!enData) {
            bRet = NO;
            break;
        }
        
        [dataOut appendData:enData];
        
        BOOL finished = NO;
        for (int i = 0; i < WY_CRYPT_SKIP_COUNT; i++) {
            cryptLength = MIN([data length] - pos, WY_CRYPT_SKIP_LENGTH);
            [dataOut appendData:[data subdataWithRange:NSMakeRange(pos, cryptLength)]];
            pos += cryptLength;
            if (cryptLength < WY_CRYPT_SKIP_LENGTH) {
                finished = YES;
                bRet = YES;
                break;
            }
        }
        if (finished)
        {
            break;
        }
    }
    
    free(buf);buf = NULL;
    
    if (!bRet) {
        dataOut = nil;
    }
    
    return dataOut;

}

+ (NSData *) encryptFile:(NSString *)inFile withKey:(NSString*)key {
    return nil;
}
+ (NSData *) decryptFile:(NSString *)inFile withKey:(NSString*)key{
    BOOL bRet = YES;
    
    NSFileManager * manager = [NSFileManager defaultManager];
    if (![manager fileExistsAtPath:inFile]) {
        return nil;
    }
    
    FILE * in = fopen([inFile cStringUsingEncoding:NSUTF8StringEncoding], "r");
    if (!in) {
        return nil;
    }

    
    char * buf = malloc(WY_CRYPT_SKIP_LENGTH);
    memset(buf, 0, WY_CRYPT_SKIP_LENGTH);
    
    NSMutableData * dataOut = [NSMutableData data];

    int length, cryptLength = 0;
    while (YES) {
        length = fread(&cryptLength, 1, sizeof(int), in);
        if (length < sizeof(int)) {
            bRet = YES;
            break;
        }
        if (cryptLength <= 0 || cryptLength > WY_CRYPT_BUFFER_LENGTH) {
            bRet = NO;
            break;
        }
        length = fread(buf, 1, cryptLength, in);
        NSData * enData = [self decryptData:[NSData dataWithBytes:buf length:length] key:key];
        if (!enData) {
            bRet = NO;
            break;
        }
        [dataOut appendData:enData];
        
        BOOL finished = NO;
        for (int i = 0; i < WY_CRYPT_SKIP_COUNT; i++) {
            length = fread(buf, 1, WY_CRYPT_SKIP_LENGTH, in);
            [dataOut appendBytes:buf length:length];
            if (length < WY_CRYPT_SKIP_LENGTH) {
                finished = YES;
                bRet = YES;
                break;
            }
        }
        if (finished)
        {
            break;
        }
    }
    
    fclose(in);
    free(buf);buf = NULL;
    
    if (!bRet) {
        dataOut = nil;
    }
    
    return dataOut;
}



@end
